
package com.studiofortress.sf.util;

import com.studiofortress.sf.graphics.display.GraphicsApplet;
import com.studiofortress.sf.graphics.display.GraphicsDisplay;
import com.studiofortress.sf.graphics.GLTexture;
import com.studiofortress.sf.graphics.GraphicsGL;
import com.studiofortress.sf.structure.MainLoop;
import com.studiofortress.sf.structure.World;
import com.studiofortress.sf.util.structure.FrameLimitEvent;
import com.studiofortress.sf.util.structure.NonBlockingMainLoop;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;

/**
 * This is a common applet class which will fit the startup requirements for
 * most games. It creates the display, mainloop and sets up to run the game all
 * within this applet class.
 * 
 * The user should extend this class, supply the required information to the
 * constructor and implement the getStartWorld method. That method returns the
 * first world that should be passed to the mainloop run method.
 * 
 * The width and height for the display will be taken from this applet.
 * 
 * @author Joseph Lenton - JosephLenton@StudioFortress.com
 * @param <G> The type of Graphics object used by the mainloop in this Applet.
 */
public abstract class CommonStartupApplet<G extends GraphicsGL> extends Applet
{
    /**
     * This will run the applet given in a standalone JFrame window. It's width
     * and height refer to the width and height of the applet when it will be
     * displayed normally.
     *
     * The width and height are passed in to simulate the width and height
     * parameters that would normally be in the applets HTML tag.
     *
     * @param applet The applet to run on it's own, cannot be null.
     * @param width The width of the applet.
     * @param height The height of the applet.
     */
    public static void runStandAlone(CommonStartupApplet<? extends GraphicsGL> applet, int width, int height)
    {
        if (applet == null) {
            throw new IllegalArgumentException("The given applet cannot be null.");
        } else if (width <= 0) {
            throw new IllegalArgumentException("The width must be greater then 0, was given: " + width);
        } else if (height <= 0) {
            throw new IllegalArgumentException("The height must be greater then 0, was given: " + height);
        }
        
        JFrame window = new JFrame();
        
        Dimension size = new Dimension( width, height );
        applet.setMinimumSize(size);
        applet.setMaximumSize(size);
        applet.setPreferredSize(size);
        
        window.add( applet );
        window.setTitle( applet.getAppletInfo() );
        window.pack();
        window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        window.setLocationByPlatform(true);
        window.setResizable(false);
        window.setVisible(true);
        window.requestFocusInWindow();

        applet.start();
    }

    private final String title;
    private final MainLoop<G> main;
    private final GraphicsDisplay display;

    /**
     * Creates a new applet with a blank title.
     */
    public CommonStartupApplet()
    {
        this("");
    }
    
    /**
     * @param title The title name for this applet. Cannot be null.
     */
    public CommonStartupApplet(String title)
    {
        if (title == null) {
            throw new IllegalArgumentException("The given title cannot be null.");
        }

        this.title = title;
        
        setLayout( new BorderLayout() );
        
        display = new GraphicsApplet( this );
        main = newMainLoop( display );
        main.addEvent( new FrameLimitEvent(60) );
        GLTexture.getTextureManager().setAutoUnload( false );
    }
    
    /**
     * This is a method you can override to alter how the mainloop is created.
     * By default this will create a NonBlockingMainLoop.
     * 
     * @param display The display that the mainloop will need to wrap.
     * @return A new mainloop for this applet to use when running.
     */
    protected MainLoop<G> newMainLoop(GraphicsDisplay display)
    {
        return new NonBlockingMainLoop<G>( display );
    }

    /**
     * @return The GraphicsDisplay inside of this applet.
     */
    public GraphicsDisplay getDisplay()
    {
        return display;
    }

    /**
     * Creates the start world and gives it to the mainloop to run.
     */
    @Override
    public void start()
    {
        main.run( getStartWorld() );
    }

    /**
     * @return The mainloop inside this applet used to run the game.
     */
    public MainLoop<G> getMainLoop()
    {
        return main;
    }

    /**
     * @return The first World that should be run in the mainloop. Should not return null.
     */
    public abstract World<G, ?> getStartWorld();

    /**
     * Stops the mainloop from running.
     */
    @Override
    public void stop()
    {
        if (main != null) {
            main.stopRunning();
        }
    }

    /**
     * @return This applets title.
     */
    @Override
    public String getAppletInfo()
    {
        return title;
    }
}
